//
//  client.c
//  reli_udp
//
//  Created by Eric Sun on 11/20/12.
//  Copyright (c) 2012 Eric Sun. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <stdint.h>
#include <time.h>
#include "packet.h"
#include "para.h"

int main(int argc, char *argv[])
{
	int sockfd, receiver_port, sender_port;
	struct sockaddr_in sender_addr, receiver_addr;
	char *sender_ip;
	char *file_name;
    double pl, pc;
    struct Packet *packet_s, *packet_r;
    size_t sender_len, receiver_len;
    unsigned int seq_expect = 0;

	// new seed generated
	srand(time(NULL));
	/* argument debug
     for(i = 0; i < argc; i++ )
     {
     printf( "arg %d: %s\n", i, argv[i] );
     }
     */
    
	// get host's ip address, port number and file to request from argument
	// or use default ones
	if (argc < 2) {
		sender_ip = SENDER_IP;
		sender_port = SENDER_PORT;
		file_name = FILE_NAME;
        pl = PL;
        pc = PC;
	}
	else if (argc == 6) {
		sender_ip = argv[1];
		sender_port = atoi(argv[2]);
		file_name = argv[3];
        pl = strtod(argv[4], NULL);
        pc = strtod(argv[5], NULL);
	}
	else {
		perror("Usage: client [hostname port filename pl pc]");
		exit(1);
	}
    
	// create socket
	if ((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
		perror("socket error");
		exit(1);
	}

	bzero(&sender_addr, sizeof(sender_addr));
	sender_addr.sin_family = AF_INET;
	sender_addr.sin_port = htons(sender_port);
	if (inet_aton(sender_ip, &sender_addr.sin_addr) == 0)
	{
		perror("server ip error");
		exit(1);
	}

	bzero(&receiver_addr, sizeof(receiver_addr));
	receiver_addr.sin_family = AF_INET;
	receiver_addr.sin_addr.s_addr = INADDR_ANY;
	receiver_addr.sin_port = htons(RECEIVER_PORT);

	// bind port
	if (bind(sockfd, (struct sockaddr *)&receiver_addr, sizeof(receiver_addr)) == -1)
	{
		perror("bind error");
		exit(1);
	}

    packet_s = malloc(PACKET_SIZE);
	packet_r = malloc(PACKET_SIZE);

	/*
	* send a request message to server
	*/
    makeReq(packet_s, file_name);
    sendPacket(sockfd, &sender_addr, packet_s);
    
	/*
	* receive data from sender
	*/	
	char recv_buffer[BUFFER_SIZE];
	FILE *fd = fopen(DEST_FILE_NAME, "w");
	if (fd == NULL) {
		perror("open error");
		exit(1);
	}
	while (1) {
		if (recvfrom(sockfd, recv_buffer, BUFFER_SIZE, 0,
					 (struct sockaddr *) &sender_addr, (int *)&sender_len) != -1) {
			memcpy(packet_r, recv_buffer, BUFFER_SIZE);
			// with pl probability, the data is lost
			printf("%s\n", "Receiving data...");
			if (lost(pl)) {
				printf("packet %u lost...\n", packet_r->seq);
				continue;
			}
			// with pc probability, the data is corrupted
			// send the expected seq number as ack number
			if (corrupted(pc)) {
				printf("Packet %u corrupted...\n", packet_r->seq);
				makeAck(packet_s, seq_expect);
				printf("Send ack packet %u\n", packet_s->ack);
				sendPacket(sockfd, &sender_addr, packet_s);
			}
			// also if the packet seq numebr isn't expected, send the expected one
			else if (packet_r->seq != seq_expect) {
				printf("Packet %u out of order...\n", packet_r->seq);
				makeAck(packet_s, seq_expect);
				printf("Send ack packet %u\n", packet_s->ack);
				sendPacket(sockfd, &sender_addr, packet_s);
			}
			// if the seq number is what we expect
			// send the new seq_expect number and write to file
			else if (packet_r->seq == seq_expect) {
				printf("Packet %u received...\n", seq_expect);
				seq_expect = packet_r->seq + packet_r->len;
				makeAck(packet_s, seq_expect);
				printf("Send ack packet %u\n", packet_s->ack);
				sendPacket(sockfd, &sender_addr, packet_s);
				size_t result = fwrite(packet_r->data, 1, packet_r->len, fd);
				// if the last packet, exit
				if (packet_r->flagEnd == 1) {
					printf("Finish receiving packets. Program exits\n");
					break;
				}
			}
		}
	}
	fclose(fd);
	close(sockfd);
}